package com.xnyy.lottery.service;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xnyy.lottery.common.exception.CustomException;
import com.xnyy.lottery.domain.dto.ParkingSpaceAddDTO;
import com.xnyy.lottery.domain.dto.ParkingSpaceConfirmDTO;
import com.xnyy.lottery.domain.dto.ParkingSpaceQueryDTO;
import com.xnyy.lottery.domain.entity.ParkingSpace;
import com.xnyy.lottery.domain.entity.Region;
import com.xnyy.lottery.domain.entity.RegisterRecord;
import com.xnyy.lottery.domain.mapper.ParkingSpaceMapper;
import com.xnyy.lottery.domain.mapper.RegisterRecordMapper;
import com.xnyy.lottery.domain.vo.MemberLotteryInfoVO;
import com.xnyy.lottery.domain.vo.ParkingSpaceVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: 车位服务类型
 * @auther: Administrator
 * @date: 2022/9/16 23:16
 */
@AllArgsConstructor
@Service
public class ParkingSpaceService extends ServiceImpl<ParkingSpaceMapper, ParkingSpace> {
    private final ParkingSpaceMapper parkingSpaceMapper;
    private final RegisterRecordMapper registerRecordMapper;
    private final RegionService regionService;
    private final TicketService ticketService;
    //用于存储要的的号中确认选位
    private static final TimedCache<String, List<Long>> lotteryCacheMap = CacheUtil.newTimedCache(10 * DateUnit.MINUTE.getMillis());

    /**
     * 初始化车位
     */
    @Transactional(rollbackFor = Exception.class)
    public void init() {
        addParkingSpace(new ParkingSpaceAddDTO("A", "负一楼", "13、17、21、25", false));
        addParkingSpace(new ParkingSpaceAddDTO("A", "负二楼", "36、42", false));
        addParkingSpace(new ParkingSpaceAddDTO("B", "负一楼", "01、03、06、13、15、19、20、21、24、34、35、36", false));
        addParkingSpace(new ParkingSpaceAddDTO("B", "负二楼", "37、39、42、44", false));
        addParkingSpace(new ParkingSpaceAddDTO("C", "", "05、06、08、12、16、18、19、20、22、23、28、29、31、33、35、36、38、39、41、42、59", false));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "01至12", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "14至19", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "21至25", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "28、29、31", false));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "33至35", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "37至39", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "41至46", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "48", false));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "50至60", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "62至66", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "68至71", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "73", false));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "75至81", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负一楼", "83至89", true));
        addParkingSpace(new ParkingSpaceAddDTO("D", "负二楼", "01至91", true));
    }

    @Transactional(rollbackFor = Exception.class)
    public void addParkingSpace(ParkingSpaceAddDTO dto) {
        Region region = new Region(null, dto.getRegion(), dto.getRegion() + "区");
        regionService.add(region);
        List<ParkingSpace> list = new ArrayList<>();
        int[] numbers = this.getNumbers(dto);
        for (int number : numbers) {
            ParkingSpace parkingSpace = new ParkingSpace();
            parkingSpace.setRegion(dto.getRegion());
            parkingSpace.setNumber(String.format("%02d", number));
            parkingSpace.setPosition(dto.getPosition());
            parkingSpace.setStatus(0);
            String describe = StrUtil.addSuffixIfNot(dto.getRegion(), "区") +
                    StrUtil.nullToEmpty(dto.getPosition()) +
                    StrUtil.addSuffixIfNot(String.format("%02d", number), "号");
            parkingSpace.setDescribe(describe);

            list.add(parkingSpace);
        }
        list.removeIf(this::existParkingSpace);
        this.saveOrUpdateBatch(list);
    }

    private int[] getNumbers(ParkingSpaceAddDTO dto) {
        int[] numbers;
        if (dto.getRange() != null && dto.getRange()) {
            numbers = StrUtil.splitToInt(dto.getNumbers(), '至');
            if (numbers.length != 2 || (numbers[0] > numbers[1])) {
                throw new CustomException(dto.getRegion() + "区车库连续车位数据错误, 格式为：1,10");
            }
            numbers = this.toContinuityNum(numbers);
        } else {
            numbers = StrUtil.splitToInt(dto.getNumbers(), '、');
        }
        return numbers;
    }

    private boolean existParkingSpace(ParkingSpace parkingSpace) {
        return this.lambdaQuery()
                .eq(ParkingSpace::getRegion, parkingSpace.getRegion())
                .eq(ParkingSpace::getPosition, parkingSpace.getPosition())
                .eq(ParkingSpace::getNumber, parkingSpace.getNumber())
                .exists();
    }

    //转换为连续性数字
    public int[] toContinuityNum(int[] numbers) {
        int start = numbers[0];
        int end = numbers[1];

        return NumberUtil.range(start, end);
    }

    public List<ParkingSpace> selectList(ParkingSpaceQueryDTO dto) {

        return parkingSpaceMapper.selectObjectList(dto);
    }

    /**
     * 摇号
     *
     * @param barCode 序号
     * @return
     */
    public MemberLotteryInfoVO lottery(String barCode) {
        if (barCode == null) {
            throw new CustomException("登记号为空！");
        }
        RegisterRecord registerRecord = registerRecordMapper.selectByBarCode(barCode);
        if (registerRecord == null) {
            throw new CustomException("登记号错误！");
        }
        if (this.isExistsParking(registerRecord.getWorkNo())) {
            throw new CustomException("该登记号已选车位，无需再摇号！");
        }
        int currentTimes = registerRecord.getCurrentTimes() == null ? 0 : registerRecord.getCurrentTimes();
        if (currentTimes >= 2) {
            throw new CustomException("选位次数:" + currentTimes);
        }
        if (registerRecord.getResult() != null && registerRecord.getResult() == 2) {
            throw new CustomException("当前登记号【barCode】已流选，不能继续摇号");
        }
        registerRecord.setCurrentTimes(currentTimes + 1);
        //fixme 这里是最后次选择，则设置结果为流选状态，在确认选位是再改为已选状态
        if (registerRecord.getCurrentTimes() >= RegisterRecord.MAX_TIMES) {
            registerRecord.setResult(2);
        }
        if (registerRecordMapper.updateById(registerRecord) <= 0) {
            throw new CustomException("摇号失败【更新摇号次数失败】");
        }
        List<ParkingSpace> list = parkingSpaceMapper.selectRandList(registerRecord.getRegion());
        list = ticketService.selectParkingSpace(barCode, list);
        if (!list.isEmpty()) {
            Collections.shuffle(list);
        }
        MemberLotteryInfoVO memberLotteryInfoVO = new MemberLotteryInfoVO();
        memberLotteryInfoVO.setBarCode(barCode);
        memberLotteryInfoVO.setRegion(registerRecord.getRegion());
        memberLotteryInfoVO.setCurrentTimes(registerRecord.getCurrentTimes());
        memberLotteryInfoVO.setRemainTimes(2 - registerRecord.getCurrentTimes());
        List<ParkingSpaceVO> parkingSpaceVOList = list.stream().map(this::toParkingSpaceVO).collect(Collectors.toList());
        memberLotteryInfoVO.setParkingSpaceList(parkingSpaceVOList);
        lotteryCacheMap.put(barCode, list.stream().map(ParkingSpace::getId).collect(Collectors.toList()));
        return memberLotteryInfoVO;
    }

    private ParkingSpaceVO toParkingSpaceVO(ParkingSpace parkingSpace) {
        ParkingSpaceVO parkingSpaceVO = new ParkingSpaceVO();
        parkingSpaceVO.setId(parkingSpace.getId());
        parkingSpaceVO.setRegion(parkingSpace.getRegion());
        parkingSpaceVO.setNumber(parkingSpace.getNumber());
        parkingSpaceVO.setPosition(parkingSpace.getPosition());
        parkingSpaceVO.setDescribe(parkingSpace.getDescribe());
        return parkingSpaceVO;
    }

    /**
     * 确定车位
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void confirm(ParkingSpaceConfirmDTO dto) {
        RegisterRecord registerRecord = registerRecordMapper.selectByBarCode(dto.getBarCode());
        if (registerRecord == null) {
            throw new CustomException("登记号错误！");
        }
        if (this.isExistsParking(registerRecord.getWorkNo())) {
            throw new CustomException("该登记号已选车位，无需再选！");
        }
        List<Long> parkingSpaceVOList = lotteryCacheMap.get(dto.getBarCode());
        if (parkingSpaceVOList == null || !parkingSpaceVOList.contains(dto.getId())) {
            throw new CustomException("请从下面车位中任选一位");
        }
        ParkingSpace parkingSpace = parkingSpaceMapper.selectById(dto.getId());
        if (parkingSpace == null) {
            throw new CustomException("未找到车位信息！");
        }
        if (parkingSpace.getStatus() != null && parkingSpace.getStatus() == 1) {
            throw new CustomException("该车位已分配！");
        }
        registerRecord.setParkingSpaceId(parkingSpace.getId());
        registerRecord.setSelectTime(new Date());
        registerRecord.setResult(1);
        if (registerRecordMapper.updateById(registerRecord) <= 0) {
            throw new CustomException("选定车位失败！！");
        }
        parkingSpace.setStatus(1);
        parkingSpace.setAllocationTime(new Date());
        parkingSpace.setRegisterId(registerRecord.getId());
        if (parkingSpaceMapper.updateById(parkingSpace) <= 0) {
            throw new CustomException("选定车位失败！！");
        }
        lotteryCacheMap.remove(dto.getBarCode());
    }

    private boolean isExistsParking(String workNo) {
        return registerRecordMapper.existsByResult(workNo, 1) > 0;
    }
}
